package com.hue.gulimail.gulimailsearch.service.impl;

import com.alibaba.fastjson.JSON;
import com.hue.common.to.es.SkuEsModel;
import com.hue.gulimail.gulimailsearch.config.ElasticSearchConfig;
import com.hue.gulimail.gulimailsearch.constant.IndexConstant;
import com.hue.gulimail.gulimailsearch.service.SearchService;
import com.hue.gulimail.gulimailsearch.vo.Param;
import com.hue.gulimail.gulimailsearch.vo.SearchResult;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.thymeleaf.util.StringUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * @author 姚凤桥
 */
@Service
public class SearchServiceImpl implements SearchService {
    @Autowired
    private RestHighLevelClient client;

    @Override
    public SearchResult search(Param param) {
        //根据查询参数构建查询Es的DSL语句

        //1.构建检索请求
        SearchRequest search = buildSearchRequest(param);
        try {
            //2.发送请求,获取结果
            SearchResponse res = client.search(search, ElasticSearchConfig.COMMON_OPTIONS);

            //3.处理响应结果
            return buildSearchResult(res, param);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 构建检索请求
     * 模糊匹配、过滤(分类id、品牌id、属性id、价格区间)
     * 排序、分页、高亮显示、聚合分析
     *
     * @param param
     * @return
     */
    private SearchRequest buildSearchRequest(Param param) {
        SearchRequest search = new SearchRequest(IndexConstant.PRODUCE_INDEX);

        SearchSourceBuilder source = new SearchSourceBuilder();

        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();

        //1.根据keyword进行模糊匹配
        String keyword = param.getKeyword();
        if (!StringUtils.isEmpty(keyword)) {
            boolQuery.must(QueryBuilders.matchQuery("skuTitle", keyword));
        }

        //2.根据catalogId、brandId、attrId来过滤
        if (param.getCatalog3Id() != null) {
            boolQuery.filter(QueryBuilders.termQuery("catalogId", param.getCatalog3Id()));
        }
        if (param.getBrandId() != null && param.getBrandId().size() > 0) {
            boolQuery.filter(QueryBuilders.termsQuery("brandId", param.getBrandId()));
        }
        if (param.getAttrs() != null && param.getAttrs().size() > 0) {
            for (String attr : param.getAttrs()) {
                //attrs=2_5寸:6寸
                String attrId = attr.split("_")[0];
                String[] attrValues = attr.split("_")[1].split(":");
                BoolQueryBuilder nestedQuery = QueryBuilders.boolQuery();
                nestedQuery.must(QueryBuilders.termQuery("attrs.attrId", attrId));
                nestedQuery.must(QueryBuilders.termsQuery("attrs.attrValue", attrValues));
                boolQuery.filter(QueryBuilders.nestedQuery("attrs", nestedQuery, ScoreMode.None));
            }
        }

        //根据是否有库存过滤
        if (param.getHasStock() != null) {
            boolQuery.filter(QueryBuilders.termQuery("hasStock", param.getHasStock() == 1));
        }

        //按照价格区间过滤
        String price = param.getSkuPrice();
        if (!StringUtils.isEmpty(price)) {
            String startPrice = !price.startsWith("_") ? price.split("_")[0] : "0";
            String endPrice = !price.endsWith("_") ? price.split("_")[1] : Integer.MAX_VALUE + "";
            boolQuery.filter(QueryBuilders.rangeQuery("skuPrice").gte(startPrice).lte(endPrice));
        }

        source.query(boolQuery);

        //添加排序规则
        String sort = param.getSort();
        if (!StringUtils.isEmpty(sort)) {
            String attr = sort.split("_")[0];
            String type = sort.split("_")[1];
            source.sort(attr, "asc".equals(type) ? SortOrder.ASC : SortOrder.DESC);
        }

        //添加分页
        Integer pageNum = param.getPageNum();
        if (pageNum == null || pageNum < 1) {
            pageNum = 1;
        }
        source.from((pageNum - 1) * IndexConstant.PAGE_SIZE);
        source.size(IndexConstant.PAGE_SIZE);

        //添加高亮显示
        if (!StringUtils.isEmpty(param.getKeyword())) {
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            highlightBuilder.field("skuTitle");
            highlightBuilder.preTags("<b style='color:red'>");
            highlightBuilder.postTags("</b>");
            source.highlighter(highlightBuilder);
        }

        //聚合分析
        //1.品牌聚合
        TermsAggregationBuilder brandIdAggregation =
                AggregationBuilders.terms("brand_agg").field("brandId").size(IndexConstant.PAGE_SIZE);
        //1.1品牌的子聚合（名字）
        brandIdAggregation.subAggregation(AggregationBuilders.terms("brand_name_agg").field("brandName").size(IndexConstant.PAGE_SIZE));
        //1.2品牌的子聚合（图片）
        brandIdAggregation.subAggregation(AggregationBuilders.terms("brand_img_agg").field("brandImg").size(IndexConstant.PAGE_SIZE));
        source.aggregation(brandIdAggregation);

        //2.分类聚合
        TermsAggregationBuilder catalogIdAggregation = AggregationBuilders.terms("catalog_agg").field("catalogId").size(IndexConstant.PAGE_SIZE);
        //2.1分类的子聚合（分类名）
        catalogIdAggregation.subAggregation(AggregationBuilders.terms("catalog_name_agg").field("catalogName").size(IndexConstant.PAGE_SIZE));
        source.aggregation(catalogIdAggregation);

        //3.属性聚合
        NestedAggregationBuilder attrsAggregation = AggregationBuilders.nested("attr_agg", "attrs");
        //3.1属性的子聚合（属性id）
        TermsAggregationBuilder attrIdAggregation = AggregationBuilders.terms("attr_id_agg").field("attrs.attrId").size(IndexConstant.PAGE_SIZE);
        //3.1.1属性id的子聚合（属性名）
        attrIdAggregation.subAggregation(AggregationBuilders.terms("attr_name_agg").field("attrs.attrName").size(IndexConstant.PAGE_SIZE));
        //3.1.2属性id的子聚合（属性值）
        attrIdAggregation.subAggregation(AggregationBuilders.terms("attr_value_agg").field("attrs.attrValue").size(IndexConstant.PAGE_SIZE));
        attrsAggregation.subAggregation(attrIdAggregation);
        source.aggregation(attrsAggregation);

        System.out.println("DSL语句:" + source);

        search.source(source);
        return search;
    }

    /**
     * 根据检索响应构建返回页面的结果
     *
     * @param res
     * @return
     */
    private SearchResult buildSearchResult(SearchResponse res, Param param) {
        SearchResult result = new SearchResult();

        SearchHits hits = res.getHits();

        //设置分页信息
        long total = hits.getTotalHits().value;
        result.setTotal(total);
        result.setTotalPage(total % IndexConstant.PAGE_SIZE == 0 ? (int) total / IndexConstant.PAGE_SIZE : (int) total / IndexConstant.PAGE_SIZE + 1);
        Integer pageNum = param.getPageNum();
        result.setPageNum(pageNum == null ? 1 : pageNum);

        //获取所有商品信息
        List<SkuEsModel> skuEsModelList = new ArrayList<>();
        if (hits.getHits() != null && hits.getHits().length > 0) {
            SearchHit[] hitsHits = hits.getHits();
            for (SearchHit searchHit : hitsHits) {
                String source = searchHit.getSourceAsString();
                SkuEsModel skuEsModel = JSON.parseObject(source, SkuEsModel.class);
                if (searchHit.getHighlightFields().containsKey("skuTitle")) {
                    skuEsModel.setSkuTitle(searchHit.getHighlightFields().get("skuTitle").getFragments()[0].string());
                }
                skuEsModelList.add(skuEsModel);
            }
        }
        result.setSkuEsModels(skuEsModelList);

        //设置品牌信息
        List<SearchResult.BrandVo> brandVos = new ArrayList<>();
        Aggregations aggregations = res.getAggregations();
        Map<String, Aggregation> aggMap = aggregations.getAsMap();
        ParsedLongTerms brandAgg = (ParsedLongTerms) aggMap.get("brand_agg");
        List<? extends Terms.Bucket> brandAggBuckets = brandAgg.getBuckets();
        for (Terms.Bucket bucket : brandAggBuckets) {
            SearchResult.BrandVo brandVo = new SearchResult.BrandVo();
            long brandId = bucket.getKeyAsNumber().longValue();
            brandVo.setBrandId(brandId);

            ParsedStringTerms brandNameAgg = bucket.getAggregations().get("brand_name_agg");
            List<? extends Terms.Bucket> brandNameAggBuckets = brandNameAgg.getBuckets();
            if (brandNameAggBuckets != null && brandNameAggBuckets.size() > 0) {
                brandVo.setBrandName(brandNameAggBuckets.get(0).getKeyAsString());
            }

            ParsedStringTerms brandImgAgg = bucket.getAggregations().get("brand_img_agg");
            List<? extends Terms.Bucket> brandImgAggBuckets = brandImgAgg.getBuckets();
            if (brandImgAggBuckets != null && brandImgAggBuckets.size() > 0) {
                brandVo.setBrandImages(brandImgAggBuckets.get(0).getKeyAsString());
            }

            brandVos.add(brandVo);
        }
        result.setBrandVos(brandVos);

        //设置分类信息
        List<SearchResult.CatalogVo> catalogVos = new ArrayList<>();
        ParsedLongTerms catalogAgg = (ParsedLongTerms) aggMap.get("catalog_agg");
        List<? extends Terms.Bucket> catalogAggBuckets = catalogAgg.getBuckets();
        if (catalogAggBuckets != null && catalogAggBuckets.size() > 0) {
            for (Terms.Bucket bucket : catalogAggBuckets) {
                SearchResult.CatalogVo catalogVo = new SearchResult.CatalogVo();
                catalogVo.setCatalogId(bucket.getKeyAsNumber().intValue());
                ParsedStringTerms catalogNameAgg = bucket.getAggregations().get("catalog_name_agg");
                List<? extends Terms.Bucket> catalogNameAggBuckets = catalogNameAgg.getBuckets();
                if (catalogNameAggBuckets != null && catalogNameAggBuckets.size() > 0) {
                    catalogVo.setCatalogName(catalogNameAggBuckets.get(0).getKeyAsString());
                }
                catalogVos.add(catalogVo);
            }
        }
        result.setCatalogVos(catalogVos);

        //设置所有属性信息
        List<SearchResult.AttrVo> attrVos = new ArrayList<>();
        ParsedNested attrAgg = (ParsedNested) aggMap.get("attr_agg");
        ParsedLongTerms attrIdAgg = attrAgg.getAggregations().get("attr_id_agg");
        List<? extends Terms.Bucket> attrIdAggBuckets = attrIdAgg.getBuckets();
        if (attrIdAggBuckets != null && attrIdAggBuckets.size() > 0) {
            for (Terms.Bucket bucket : attrIdAggBuckets) {
                SearchResult.AttrVo attrVo = new SearchResult.AttrVo();
                attrVo.setAttrId(bucket.getKeyAsNumber().intValue());

                ParsedStringTerms attrNameAgg = bucket.getAggregations().get("attr_name_agg");
                List<? extends Terms.Bucket> nameAggBuckets = attrNameAgg.getBuckets();
                if (nameAggBuckets != null && nameAggBuckets.size() > 0) {
                    attrVo.setAttrName(nameAggBuckets.get(0).getKeyAsString());
                }

                ParsedStringTerms attrValueAgg = bucket.getAggregations().get("attr_value_agg");
                List<? extends Terms.Bucket> valueAggBuckets = attrValueAgg.getBuckets();
                if (valueAggBuckets != null && valueAggBuckets.size() > 0) {
                    List<String> values = new ArrayList<>();
                    for (Terms.Bucket valueBucket : valueAggBuckets) {
                        values.add(valueBucket.getKeyAsString());
                    }
                    attrVo.setAttrValue(values);
                }
                attrVos.add(attrVo);
            }
        }
        result.setAttrVos(attrVos);

        return result;
    }
}
